/**
 * 2D / UI 节点必须放在 Canvas 下面才会显示（实际上是 RenderRoot2D，因为 Canvas 继承自 RenderRoot2D）
 * 小心规划物体的层级，需要调整相机的 Visiblity 属性来让不同的 Canvas 分开渲染
 * */

import { _decorator, Component, EventKeyboard, EventMouse, Input, input, Node, Vec3, Animation } from 'cc';
const { ccclass, property } = _decorator;

export const BLOCK_SIZE = 40; // 添加一个放大比

@ccclass('PlayerController')
export class PlayerController extends Component {
    @property(Animation) // 这里的装饰器 @property 可以帮助编辑器，使其将 BodyAnim 在编辑器内视为 Animation 类型
    BodyAnim:Animation = null;

    private _startJump: boolean = false; // 是否开始跳跃 用于判断角色是否在跳跃状态
    private _jumpStep: number = 0; // 跳跃步数：一步或者两步 用于记录鼠标的输入，并将其转化为数值。因为我们规定角色最多只能跳两步，那么他可能是 1 或者 2
    private _curJumpTime: number = 0; // 当前的跳跃时间 每次跳跃前，将这个值置为 0，在更新时进行累计并和 _jumpTime 进行对比，如果超过了 _jumpTime，那么我们认为角色完成了一次完整的跳跃
    private _jumpTime: number = 0.1; // 跳跃时间 这个数值类型的变量用于记录整个跳跃的时长
    private _curJumpSpeed: number = 0; // 移动速度 用于记录跳跃时的移动速度
    private _curPos: Vec3 = new Vec3(); // 当前的位置 记录和计算角色的当前位置
    private _deltaPos: Vec3 = new Vec3(0, 0, 0); // 位移 每一帧我们都需要记录下位置和时间间隔的乘积，我们将用他来存储计算结果
    private _targetPos: Vec3 = new Vec3(); // 目标位置 最终的落点，我们将在跳跃结束时将角色移动这个位置以确保最终的位置正确，这样可以处理掉某些误差的情况
    private _curMoveIndex: number = 0; // 用于记录角色当前为多少步
    
    start() {}

    setInputActive(active: boolean) {
        if (active) {
            input.on(Input.EventType.MOUSE_UP, this.onMouseUp, this); // 监听鼠标输入
            input.on(Input.EventType.KEY_UP, this.onKeyboardUp, this); // 监听键盘输入
        } else {
            input.off(Input.EventType.MOUSE_UP, this.onMouseUp, this); // 注销监听
            input.off(Input.EventType.KEY_UP, this.onKeyboardUp, this); // 注销监听
        }
    }

    // 监听鼠标输入
    onMouseUp(e: EventMouse) {
        const getButton = e.getButton()
        if (getButton === 0) {
            // 点击了鼠标左键, 跳一步
            this.jumpByStep(1)
        } else if (getButton === 2) {
            // 点击了鼠标右键, 跳两步
            this.jumpByStep(2)
        }
    }

    // 监听键盘输入
    onKeyboardUp(e: EventKeyboard) {
        const getKeyCode = e.keyCode
        if (getKeyCode === 37) {
            // 键盘向左
            console.log('左')
        } else if (getKeyCode === 38) {
            // 键盘向上
            console.log('上')
            this.jumpByStep(2)
        } else if (getKeyCode === 39) {
            // 键盘向
            console.log('右')
            this.jumpByStep(1)
        } else if (getKeyCode === 40) {
            // 键盘向
            console.log('下')
        }
    }

    // 根据步数跳跃
    jumpByStep(step: number) {
        if (this._startJump) return; // 如果当着在跳跃，则返回
        this._startJump = true; // 标记开始跳跃
        this._jumpStep = step; // 跳跃的步数 1 或者 2
        this._curJumpTime = 0; // 移动速度

        const clipName = step == 1 ? 'oneStep' : 'twoStep';
        // 通过获取动画剪辑的时长来动态调整 _jumpTime
        const state = this.BodyAnim.getState(clipName);
        this._jumpTime = state.duration;

        this._curJumpSpeed = this._jumpStep * BLOCK_SIZE / this._jumpTime // 根据时间计算出速度
        this.node.getPosition(this._curPos) // 获取角色当前的位置
        Vec3.add(this._targetPos, this._curPos, new Vec3(this._jumpStep * BLOCK_SIZE, 0, 0)) // 计算出目标位置

        if (this.BodyAnim) {
            if (step === 1) {
                this.BodyAnim.play('oneStep');
            } else if (step === 2) {
                this.BodyAnim.play('twoStep');
            }
        }
        this._curMoveIndex += step;
    }

    // 根据每次的更新来计算角色最新的位置
    update(deltaTime: number) {
        if (this._startJump) {
            this._curJumpTime += deltaTime; // 累计总的跳跃时间
            if (this._curJumpTime > this._jumpTime) { // 当跳跃时间是否结束
                this.node.setPosition(this._targetPos); // // 强制位置到终点
                this._startJump = false // 清理跳跃标记
                this.onOnceJumpEnd();
            } else {
                this.node.getPosition(this._curPos);
                this._deltaPos.x = this._curJumpSpeed * deltaTime; // 每一帧根据速度和时间计算位移
                Vec3.add(this._curPos, this._curPos, this._deltaPos); // 应用这个位移
                this.node.setPosition(this._curPos) // 将位移设置给角色
            }
        }
    }

    // 重置
    reset() {
        this._curMoveIndex = 0;
    }

    // 监听跳跃结束的方法
    onOnceJumpEnd() {
        this.node.emit('JumpEnd', this._curMoveIndex);
    }
}

